
#include "statistics.h"


namespace	memory
{
	statistics::statistics()
		: heaps_( NULL )
		, heap_count_( 0 )
		, caches_( NULL )
		, cache_count_( 0 )
	{
	}

	statistics::~statistics()
	{
		destroy();
	}

	bool	statistics::create( bool enable, size_t heap_count, size_t cache_count, size_t lookaside_count )
	{
		assert( heaps_ == NULL );
		assert( caches_ == NULL );
		if( heaps_!= NULL || caches_ != NULL )
		{
			return false;
		}

		if( enable )
		{
			heap_count_ = heap_count;
			if( heap_count_ > 0 )
			{
				heaps_ = new heap_statistics_t[heap_count_];
				if( heaps_ == NULL )
				{
					return false;
				}
			}

			cache_count_ = cache_count;
			if( cache_count_ > 0 && lookaside_count > 0 )
			{
				caches_ = new cache_statistics_t[cache_count_];
				if( caches_ == NULL )
				{
					return false;
				}

				for( size_t i = 0; i < cache_count_; ++i )
				{
					caches_[i].count = lookaside_count;
					caches_[i].lookaside = new lookaside_statistics_t[lookaside_count];
					if( caches_[i].lookaside == NULL )
					{
						return false;
					}
				}
			}

			clear();
		}

		return true;
	}

	void	statistics::destroy()
	{
		if( heaps_ != NULL )
		{
			delete [] heaps_;
			heaps_ = NULL;
		}
		heap_count_ = 0;

		if( caches_ != NULL )
		{
			for( size_t i = 0; i < cache_count_; ++i )
			{
				delete [] caches_[i].lookaside;
				caches_[i].lookaside = NULL;
			}

			delete [] caches_;
			caches_ = NULL;
		}
		cache_count_ = 0;
	}

	void	statistics::clear()
	{
		if( heaps_ != NULL )
		{
			::memset( heaps_, 0, sizeof( heap_statistics_t ) * heap_count_ );
		}

		if( caches_ != NULL )
		{
			for( size_t i = 0; i < cache_count_; ++i )
			{
				::memset( caches_[i].lookaside, 0, sizeof( lookaside_statistics_t ) * caches_[i].count );
			}
		}
	}

	void	statistics::inc_heap_alloc( size_t idx )
	{
		heap_statistics_t* heap = get_heap( idx );
		if( heap != NULL )
		{
			::InterlockedIncrement( &heap->alloc );
		}
	}

	void	statistics::inc_heap_free( size_t idx )
	{
		heap_statistics_t* heap = get_heap( idx );
		if( heap != NULL )
		{
			::InterlockedIncrement( &heap->free );
		}
	}

	void	statistics::inc_cache_alloc( size_t cache_idx, size_t las_idx )
	{
		lookaside_statistics_t* lookaside = get_lookaside( cache_idx, las_idx );
		if( lookaside != NULL )
		{
			::InterlockedIncrement( &lookaside->alloc );
		}
	}

	void	statistics::inc_cache_free( size_t cache_idx, size_t las_idx )
	{
		lookaside_statistics_t* lookaside = get_lookaside( cache_idx, las_idx );
		if( lookaside != NULL )
		{
			::InterlockedIncrement( &lookaside->free );
		}
	}

	size_t	statistics::get_heap_count()	const
	{
		return heap_count_;
	}

	size_t	statistics::get_cache_count()	const
	{
		return cache_count_;
	}

	const heap_statistics_t*	statistics::get_heap( size_t idx )	const
	{
		const heap_statistics_t* heap = NULL;
		if( heaps_ != NULL )
		{
			if( 0 <= idx && idx < heap_count_ )
			{
				heap = &heaps_[idx];
			}
		}

		return heap;
	}

	const cache_statistics_t*	statistics::get_cache( size_t cache_idx )	const
	{
		const cache_statistics_t* cache = NULL;
		if( caches_ != NULL )
		{
			if( 0 <= cache_idx && cache_idx < cache_count_ )
			{
				cache = &caches_[cache_idx];
			}
		}

		return cache;
	}

	heap_statistics_t*	statistics::get_heap( size_t idx )
	{
		heap_statistics_t* heap = NULL;
		if( heaps_ != NULL )
		{
			if( 0 <= idx && idx < heap_count_ )
			{
				heap = &heaps_[idx];
			}
		}

		return heap;
	}

	lookaside_statistics_t*	statistics::get_lookaside( size_t cache_idx, size_t las_idx )
	{
		cache_statistics_t* cache = NULL;
		if( caches_ != NULL )
		{
			if( 0 <= cache_idx && cache_idx < cache_count_ )
			{
				cache = &caches_[cache_idx];
			}
		}

		lookaside_statistics_t* lookaside = NULL;
		if( cache != NULL )
		{
			if( 0 <= las_idx && las_idx < cache->count )
			{
				lookaside = &cache->lookaside[las_idx];
			}
		}

		return lookaside;
	}
}
